home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / x2ftp / msdos / mxcode / sb_pas10 / play_voc.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1993-01-25  |  12.4 KB  |  351 lines

  1. Program Play_Voc;
  2. { Demonstrates the use of SB_Unit by playing a VOC file from disk using DMA }
  3. { double-buffering.                                                         }
  4.  
  5. { Options to this program are given if it is used without parameters. Only  }
  6. { .VOC files with a single block of data are supported; others may also     }
  7. { work, but any block information will be output as voice data.             }
  8. { Also, samples at rates > 22.05kHz seem to use a header format I don't     }
  9. { have information about. The program flags these and displays an error.    }
  10.  
  11. Uses CRT,DOS,SB_Unit;
  12.  
  13. Const
  14.    DEFAULT_BUFFER : word = $8000;
  15.    DEFAULT_IRQ : byte = 5;
  16.    DEFAULT_DMA : byte = 1;
  17.  
  18. Var
  19.    FileStr : String[80];                  { Filename parameter string.      }
  20.    FileName : Array[1..80] of char;       { ASCIZ converted filename.       }
  21.    Loop : integer;                        { Misc. loop counter.             }
  22.    FullFilename : boolean;                { Flag for filename extension.    }
  23.    FileHandle : word;                     { File handle (returned by DOS).  }
  24.    Buffer1, Buffer2 : pointer;            { Double buffers.                 }
  25.    Size : word;                           { Size of disk read.              }
  26.    ReadError : byte;                      { Disk read error code.           }
  27.    Base : word;                           { Base address of card.           }
  28.    IRQ,                                   { Card IRQ.                       }
  29.    DMA : byte;                            { Card DMA channel.               }
  30.    Blaster,                               { "BLASTER" environment variable. }
  31.    Param : string[20];                    { Command line parameter.         }
  32.    Err : integer;                         { "Val" function error code.      }
  33.    BufferSize : word;                     { Size of buffers.                }
  34.    VOCHeader : Array[0..18] of char;      { Header for VOC file.            }
  35.    SampleRate : word;                     { Playback rate (in Hz).          }
  36.  
  37.  
  38. Procedure Instructions;
  39. { Outputs instructions for command line usage.                              }
  40.    Begin
  41.       Writeln('Usage : PLAY_VOC <filename>[.VOC] [options]');
  42.       Writeln;
  43.    end;
  44.  
  45.  
  46. Procedure ReadBuffer(Handle : word; Buffer : pointer; Var Size : word;
  47.                      Var Error : byte);
  48.       assembler;
  49. { Reads Size bytes from file Handle into Buffer. Size returns with the      }
  50. { actual number of bytes read, and Error returns with error code (if any).  }
  51.  
  52.       asm
  53.          cld                              { Forward string moves.           }
  54.          push  ds                         { Can't modify ds.                }
  55.          mov   ax,word ptr Buffer+2       { Segment of Buffer pointer.      }
  56.          mov   ds,ax
  57.          mov   dx,word ptr Buffer         { Offset of Buffer pointer.       }
  58.          mov   bx,Handle                  { bx = File handle.               }
  59.          mov   es,word ptr Size+2         { es:si -> Size                   }
  60.          mov   si,word ptr Size
  61.          mov   di,word ptr Error          { <seg>:di -> Error               }
  62.          mov   cx,es:[si]                 { cx = Size.                      }
  63.          mov   ah,3Fh                     { DOS - read from file.           }
  64.          int   21h
  65.          jnc   @@Success
  66.          @@Fail:
  67.             mov   es,word ptr Error+2     { Return error code.              }
  68.             mov   es:[di],al
  69.             jmp   @@Finish
  70.          @@Success:
  71.             mov   es:[si],ax              { Return actual number read.      }
  72.             mov   es,word ptr Error+2
  73.             mov   al,0                    { No error.                       }
  74.             mov   es:[di],al
  75.          @@Finish:
  76.             pop   ds
  77.       end;
  78.  
  79.  
  80. Procedure SwapBuffers(Var BufferA, BufferB : pointer);
  81.    Var
  82.       Temp : pointer;
  83.    Begin
  84.       Temp := BufferA;
  85.       BufferA := BufferB;
  86.       BufferB := Temp;
  87.    end;
  88.  
  89.  
  90. Begin
  91.    ClrScr;
  92.  
  93.    { If no command line parameters, output instructions and exit. }
  94.    If (ParamCount = 0)
  95.       then
  96.          Begin
  97.             Instructions;
  98.             Halt(1);
  99.          end;
  100.  
  101.    { Convert Pascal-style filename string to null-terminated. }
  102.    FullFilename := FALSE;
  103.    FileStr := ParamStr(1);
  104.    For Loop := 1 to Ord(FileStr[0]) do
  105.       Begin
  106.          FileName[Loop] := FileStr[Loop];
  107.          If FileName[Loop] = '.'
  108.             then
  109.                FullFilename := TRUE;
  110.       end;
  111.  
  112.    Inc(Loop);
  113.    { If suffix not given, add .VOC. }
  114.    If Not(FullFilename)
  115.       then
  116.          Begin
  117.             FileName[Loop] := '.';
  118.             Filename[Loop+1] := 'V';
  119.             FileName[Loop+2] := 'O';
  120.             Filename[Loop+3] := 'C';
  121.             Inc(Loop,4);
  122.          end;
  123.    FileName[Loop] := #0;     { Null tail. }
  124.  
  125.    { Use DOS services to open the file and return a file handle. Block      }
  126.    { reads can then be performed (much more efficient).                     }
  127.    asm
  128.       push  ds                         { Can't modify ds.                   }
  129.       mov   ax,seg FileName
  130.       mov   ds,ax
  131.       mov   dx,offset FileName         { ds:dx -> ASCIZ filename.           }
  132.       mov   ax,3D00h
  133.       int   21h                        { DOS - open file.                   }
  134.       jnc   @@Success
  135.       @@Fail:
  136.          mov   [ReadError],al          { Return the error code.             }
  137.          jmp   @@Finish
  138.       @@Success:
  139.          mov   [FileHandle],ax         { Return the file handle.            }
  140.          mov   [ReadError],0           { No error.                          }
  141.       @@Finish:
  142.          pop   ds
  143.    end;
  144.  
  145.    { Check if given a valid filename. }
  146.    If (ReadError <> 0)
  147.       then
  148.          Begin
  149.             Writeln('Invalid filename: ',Paramstr(1));
  150.             Halt(2);
  151.          end;
  152.  
  153.    { Check if file is a .VOC. }
  154.    Size := 19;
  155.    ReadBuffer(FileHandle,@VOCHeader,Size,ReadError);
  156.    If Not(VOCHeader = 'Creative Voice File')
  157.       then
  158.          Begin
  159.             Writeln('Not a valid .VOC file.');
  160.             Halt(2);
  161.          end;
  162.  
  163.    { Read header data (see VOC-INFO.TXT for details). If the first block    }
  164.    { is data, read the sample rate and blindly assume no headers follow.    }
  165.    Size := 13;
  166.    ReadBuffer(FileHandle,@VOCHeader,Size,ReadError);
  167.    If (VOCHeader[7] = #1)
  168.       then
  169.          { First block is data - find sample rate. }
  170.          SampleRate := word (VOCHeader[11])
  171.       else
  172.          Begin
  173.             Writeln('Unable to play .VOC files of this type.');
  174.             Halt(4);
  175.          end;
  176.  
  177.    { File pointer is now positioned at start of data (hopefully?). }
  178.  
  179.    { Sample rate is in Blaster byte format - convert to Hz. }
  180.    SampleRate := word (1000000 div (256 - SampleRate));
  181.  
  182.    { Set defaults for IRQ and DMA channel. These are used if nothing new    }
  183.    { is defined.                                                            }
  184.    Base := 0;              { If no other base address is found, autodetect. }
  185.    IRQ := DEFAULT_IRQ;
  186.    DMA := DEFAULT_DMA;
  187.    BufferSize := DEFAULT_BUFFER;
  188.  
  189.    { Second source for settings : "BLASTER" environment variable. }
  190.    Blaster := GetEnv('BLASTER');
  191.    Err := 0;
  192.    While (Blaster <> '') and (Err = 0) do
  193.       Case Blaster[1] of
  194.          'A','a' : Begin         { Base address. }
  195.                       Val(Blaster[2]+Blaster[3]+Blaster[4],Base,Err);
  196.                       Base := ((Base mod 200) div 10)*16 + 512;
  197.                       If (Err = 0)
  198.                         then
  199.                            Blaster := Copy(Blaster,5,Length(Blaster)-4);
  200.                    end;
  201.          'I','i' : Begin         { IRQ }
  202.                       Val(Blaster[2],IRQ,Err);
  203.                       If (Err = 0)
  204.                         then
  205.                            Blaster := Copy(Blaster,3,Length(Blaster)-2);
  206.                    end;
  207.          'D','d' : Begin         { DMA channel. }
  208.                       Val(Blaster[2],DMA,Err);
  209.                       If (Err = 0)
  210.                         then
  211.                            Blaster := Copy(Blaster,3,Length(Blaster)-2);
  212.                    end;
  213.          else    { Step past extraneous characters. }
  214.             Blaster := Copy(Blaster,2,Length(Blaster)-1);
  215.       end;
  216.  
  217.    { Check command line switches. }
  218.    For Loop := 2 to Paramcount do
  219.       Begin
  220.          Param := ParamStr(Loop);
  221.          Case Param[2] of
  222.             'A','a' : Begin
  223.                          Val(Param[3]+Param[4]+Param[5],Base,Err);
  224.                          Base := ((Base mod 100) div 10)*16 + 512;
  225.                       end;
  226.             'I','i' : Val(Param[3],IRQ,Err);
  227.             'D','d' : Val(Param[3],DMA,Err);
  228.             'B','b' : Begin
  229.                          Case Length(Param) of
  230.                             3 : Val(Param[3],BufferSize,Err);
  231.                             4 : Val(Param[3],BufferSize,Err);
  232.                             else
  233.                                Err := 1;
  234.                          end;
  235.                          If ((BufferSize < 1) OR (BufferSize > 64))
  236.                            then
  237.                               Err := 1
  238.                            else
  239.                               BufferSize := (BufferSize-1)*$400 + $3F0;
  240.                       end;
  241.             else
  242.                Err := 1;
  243.             end;
  244.          If (Err <> 0)
  245.             then
  246.                Begin
  247.                   Writeln('Invalid switch : ',Param);
  248.                   Halt(3);
  249.                end;
  250.       end;
  251.  
  252.    If (Base = 0)
  253.       then
  254.          SB_DetectBase(Base);
  255.  
  256.    { Correct Base address, IRQ, DMA channel, and buffer size found. }
  257.  
  258.    SB_SetBaseAddr(Base);
  259.    If Not(SB_DSPReset(Base))
  260.       then
  261.          Begin
  262.             Writeln('Unable to initialize Sound Blaster.');
  263.             Halt(4);
  264.          end;
  265.    SB_SetIRQ(IRQ);
  266.    SB_SetDMAChannel(DMA);
  267.  
  268.    { Reset DSP (only necessary if not autodetected). }
  269.    SB_DSPReset(Base);
  270.  
  271.    { Set speaker on for output. }
  272.    SB_Speaker(1);
  273.  
  274.    { Since using DMA, set the interrupt handler. }
  275.    SB_SetIntHandler;
  276.  
  277.    { Create two buffers - one will be filled with data from disk while the  }
  278.    { other plays.                                                           }
  279.    SB_MakeDMABuffer(Buffer1,BufferSize div $10);
  280.    SB_MakeDMABuffer(Buffer2,BufferSize div $10);
  281.  
  282.    { Make sure buffers were created successfully. }
  283.    If ((Buffer1 = nil) OR (Buffer2 = nil))
  284.       then
  285.          Begin
  286.             Writeln('Unable to allocate buffers.');
  287.             Halt(5);
  288.          end;
  289.  
  290.    { Ready to start playing. }
  291.    Writeln('Press any key to end playing...');
  292.    Writeln;
  293.  
  294.    ReadError := 0;               { No file read errors (yet).               }
  295.    SB_DMAComplete := TRUE;       { This is to pass the first sentinel loop. }
  296.    Size := BufferSize;           { Actual size read from disk.              }
  297.    Repeat
  298.       { Fill first buffer with data. }
  299.       ReadBuffer(FileHandle,Buffer1,Size,ReadError);
  300.       { The actual number of bytes read is returned to Size. }
  301.  
  302.       { Flag any read errors (there shouldn't be any, but...). }
  303.       If (ReadError <> 0)
  304.          then
  305.             Begin
  306.                Writeln('File read error.');
  307.                Halt(2);
  308.             end;
  309.  
  310.       { Sentinel loop - waits for one buffer to finish playing before       }
  311.       { passing to the new data. (Skips loop first time because of          }
  312.       { SB_DMAComplete assignment above).                                   }
  313.       Repeat
  314.       Until ((SB_DMAComplete) OR (Keypressed));
  315.  
  316.       { Make sure playback was not aborted before playing next buffer. }
  317.       If Not(Keypressed)
  318.          then
  319.             { Make sure to use actual amount of data read (Size). }
  320.             SB_PlayDMA(Buffer1,SampleRate,Size);
  321.  
  322.       { Exchange the buffers. }
  323.       SwapBuffers(Buffer1,Buffer2);
  324.       { Buffer2 is now playing, and Buffer1 will be filled with new data.   }
  325.  
  326.    { End when the buffer is not completely filles from disk. }
  327.    Until ((Size <> BufferSize) OR (Keypressed));
  328.  
  329.    { Wait for final buffer to finish playing. }
  330.    Repeat
  331.    Until ((SB_DMAComplete) OR (Keypressed));
  332.  
  333.    { Needed for an abort, but can't hurt either way. }
  334.    SB_StopDMA;
  335.  
  336.    If (Keypressed)
  337.       then
  338.          Writeln('Playback aborted.')
  339.       else
  340.          Writeln('Playback complete.');
  341.  
  342.  
  343.    { That's it. Do some routine cleaning. }
  344.    { After an aborted transfer, always reset the DSP before other functions.}
  345.    SB_DSPReset(Base);
  346.    SB_Speaker(0);
  347.  
  348.    SB_ResetIntHandler;
  349.  
  350. end.
  351.